package prod_cons;

import java.util.Random;

/**
 * Defines static methods for working with metadata.
 * Each meta has the following structure:
 * Bits 0-19 -- version
 * Bit 20 -- "rendezvous" point for putter and getter
 * Bit 21 -- dirty bit - equals '1' if the node has been already used
 * Bit 30 -- equals '1' if there are tasks in the left subtree
 * Bit 31 -- equals '1' if there are tasks in the right subtree
 */
public class HeightCalculation {
	private static final int RIGHT_MASK = 1; 	// 0001
	private static final int LEFT_MASK = 2;		// 0010
	private static final int PENDING_MASK = 0x000003fc;  // 0011 1111 1100
	private static final int DIRTY_MASK = 1 << 10;       // 0100 0000 0000
	private static final int VERSION_MASK = 0xfffff000;  // 1111 1111 1111 1111 1111 0000 0000 0000
	
	private static final int RIGHT_SHIFT = 0;
	private static final int LEFT_SHIFT = 1;
	private static final int PENDING_SHIFT = 2;
	private static final int VERSION_SHIFT = 12;
	
	public static boolean isDirty(int metadata) {
		return ((metadata & DIRTY_MASK) != 0);
	}
	public static int version(int metadata) {
		return (metadata & VERSION_MASK) >> VERSION_SHIFT;
	}
	public static int setDirtyIncVersion(int metadata) {
		int result = (metadata | DIRTY_MASK);

		int version = version(result) + 1;
		result = (result & (~VERSION_MASK)) | (version << VERSION_SHIFT);
		return result;
	}
	public static boolean rightTasks(int metadata) {
		return (((metadata & RIGHT_MASK) >> RIGHT_SHIFT) != 0);
	}
	public static boolean leftTasks(int metadata) {
		return (((metadata & LEFT_MASK) >> LEFT_SHIFT) != 0);
	}
	public static boolean childTasks(int metadata, int pos) {
		if (pos == 0) return leftTasks(metadata);
		return rightTasks(metadata);
	}
	public static boolean childrenTasks(int metadata) {
		return (leftTasks(metadata) || rightTasks(metadata));
	}
	public static int childTasksIncVersion(int metadata, int pos, int newVal) {
		int result = (pos == 0) ? (metadata & (~LEFT_MASK)) | (newVal << LEFT_SHIFT) : 
			(metadata & (~RIGHT_MASK)) | (newVal << RIGHT_SHIFT);

		int version = version(result) + 1;
		result = (result & (~VERSION_MASK)) | (version << VERSION_SHIFT);
		return result;
	}
	public static int getPending(int metadata) {
		return ((metadata & PENDING_MASK) >> PENDING_SHIFT);
	}
	public static int incPending(int metadata) {
		int newPending = ((metadata & PENDING_MASK) >> PENDING_SHIFT) + 1;
		return (metadata & (~PENDING_MASK)) | (newPending << PENDING_SHIFT);
	}
	public static int decPending(int metadata) {
		int newPending = ((metadata & PENDING_MASK) >> PENDING_SHIFT) - 1;
		return (metadata & (~PENDING_MASK)) | (newPending << PENDING_SHIFT);
	}
	/*
	 * -1: self, 0: left, 1: right
	 */
//	public static int nextChoice(Random random, boolean self, int meta) {
//		boolean leftTasks = leftTasks(meta);
//		boolean rightTasks = rightTasks(meta);
//		if (self == true && (!leftTasks && !rightTasks)) return -1;
//
//		if (leftTasks && !rightTasks) return 0;
//		if (!leftTasks && rightTasks) return 1;
//		return random.nextInt(2);
//	}
	public static int nextChoice(Random random, boolean self, int meta) {
		boolean leftTasks = leftTasks(meta);
		boolean rightTasks = rightTasks(meta);
		if (self == true || (!leftTasks && !rightTasks)) return -1;

		if (leftTasks && !rightTasks) return 0;
		if (!leftTasks && rightTasks) return 1;
		return random.nextInt(2);
	}
	
	public static int nextChoice_algoVersion(Random random, boolean self, int meta) {
		boolean leftTasks = leftTasks(meta);
		boolean rightTasks = rightTasks(meta);
		if (self == true && (!leftTasks && !rightTasks)) return -1;
		if (!self && leftTasks && !rightTasks) return 0;
		if (!self && !leftTasks && rightTasks) return 1;		
		if (leftTasks && !rightTasks) return random.nextBoolean() ? -1 : 0;
		if (!leftTasks && rightTasks) return random.nextBoolean() ? -1 : 1;
		return (random.nextInt(3) - 1);		
	}

}
